package random

import (
	"encoding/json"
	"google.golang.org/grpc/balancer"
	"google.golang.org/grpc/balancer/base"
	"log"
	"net/http"
	"runtime"
	"sync"
	"time"
)

type Builder struct {
	tickTime time.Duration
	connList []*Conn
}

type Response struct {
	Data map[string]time.Duration
}

func (b *Builder) updateTime() {
	resp, err := http.Get("http://premethes")
	if err != nil {
		log.Println(err)
		return
	}

	defer resp.Body.Close()
	var result *Response
	err = json.NewDecoder(resp.Body).Decode(&result)
	if err != nil {
		log.Println(err)
		return
	}

	for _, conn := range b.connList {
		for key, dur := range result.Data {
			if conn.addr == key {
				conn.respTime = dur
			}
		}
	}
}

func (b *Builder) Build(info base.PickerBuildInfo) balancer.Picker {
	connList := make([]*Conn, 0, len(info.ReadySCs))
	for subConn, sc := range info.ReadySCs {
		connList = append(connList, &Conn{
			conn:     subConn,
			addr:     sc.Address.String(),
			respTime: time.Second * 100,
		})
	}

	closer := make(chan struct{})
	ticker := time.NewTicker(b.tickTime)

	go func() {
		for {
			select {
			case <-closer:
				return
			case <-ticker.C:
				b.updateTime()
			}
		}
	}()

	bl := &Balancer{
		connList: connList,
	}

	runtime.SetFinalizer(bl, func() {
		close(closer)
	})

	return bl
}

type Balancer struct {
	connList []*Conn
	mu       sync.Mutex
}

func (b *Balancer) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
	if len(b.connList) == 0 {
		return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
	}
	// 最小活跃数
	b.mu.Lock()
	defer b.mu.Unlock()

	conn := b.connList[0]
	for _, c := range b.connList {
		if c.respTime <= conn.respTime {
			conn = c
		}
	}

	return balancer.PickResult{
		SubConn: conn.conn,
		Done: func(info balancer.DoneInfo) {
			if info.Err != nil {
				log.Println(info.Err)
				return
			}
		},
	}, nil
}

type Conn struct {
	conn     balancer.SubConn
	addr     string
	respTime time.Duration
}
